package net.soe;

import net.soe.__Shape._;
import net.soe.__Region._;

//import java.awt._
import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Dimension;
import java.awt.Graphics;
import java.awt.Graphics2D;
import java.awt.event._
import java.awt.geom.AffineTransform;
import java.awt.geom.Area;
import java.awt.geom.Ellipse2D;
import java.awt.geom.Rectangle2D;

import javax.swing._


object __Picture {
	
def maxXScreenSize=5000
def maxYScreenSize=5000
abstract class _Picture {
	def Over(p: _Picture) : _Picture = {
			new Over(this, p);
	}
}
case class Region(a: _Color, b: _Region) extends _Picture
case class Over( a: _Picture, b: _Picture) extends _Picture
case class EmptyPic() extends _Picture


sealed case class _Color(javaColor:java.awt.Color)
case object Black extends _Color(java.awt.Color.BLACK)
case object Blue extends _Color(java.awt.Color.BLUE)
case object Green extends _Color(java.awt.Color.GREEN)
case object Cyan extends _Color(java.awt.Color.CYAN)
case object Red extends _Color(java.awt.Color.RED)
case object Magenta extends _Color(java.awt.Color.MAGENTA)
case object Yellow extends _Color(java.awt.Color.YELLOW)
case object White extends _Color(java.awt.Color.WHITE)

def draw( title: String, picture: _Picture) {
	 SwingUtilities.invokeAndWait(new PicturePanel(title, picture))
}


def regionToArea(region: _Region) : Area =  {
		region match {
			case Shape(s) => shapeToArea(s)
			case Translate(a: Vector, b:_Region)  =>  {			
				println("doing translation...")
				 val at = new AffineTransform();
				 at.setToTranslation( inchToPixel(a._1), -inchToPixel(a._2))  // to translate in y axis as -1 is to add 1
				 val area: Area = regionToArea(b)
				 area.transform(at)
				 area
			}
			case  Scale(a: Vector, b:_Region)  => {	
				println("Doing the scale: " + a._1 + " and " + a._2)
				 val at = new AffineTransform();
				 at.setToIdentity()
				 at.setToScale(a._1, a._2)
				 val area: Area = regionToArea(b)
				 area.transform(at)
				 area
			}
			case  Complement(a: _Region)  => {
				val win = new Area( new Rectangle2D.Double(-maxXScreenSize, -maxYScreenSize, maxXScreenSize*2, maxYScreenSize*2))
				win.subtract(regionToArea(a))
				win
			}
			case  Union(a:_Region, b:_Region)  => {
				val left = regionToArea(a)
				left.add(regionToArea(b))
				left
			}
			case  Intersect(a: _Region, b:_Region)  =>{
				val left = regionToArea(a)
				left.intersect(regionToArea(b))
				left
			}
			case  Xor(a:_Region, b:_Region)  => {
				val left = regionToArea(a)
				left.exclusiveOr(regionToArea(b))
				left
			}
			case  Empty()  => return new Area()  
		}
}

def inchToPixel(inch: Double):Double = inch*100

def shapeToArea(sh: _Shape) : Area = {
		println("Got to shapeToArea" + sh)
		sh match {
			case  Rectangle(a: Side, b:Side) => {
				new Area( new Rectangle2D.Double(-inchToPixel(a)/2,-inchToPixel(b)/2, inchToPixel(a), inchToPixel(b)))
			}
			case Ellipse(a: Radius, b:Radius) =>  {
				// NOte:  awt requires w and h which is twice the radius
				new Area(new Ellipse2D.Double(-inchToPixel(a),-inchToPixel(b), 2*inchToPixel(a), 2*inchToPixel(b)))
			}
			case  RtTriangle(a: Side, b:Side) =>  {					
//					val p = new java.awt.Polygon() ;
//					p.addPoint(0,0)
//					p.addPoint(inchToPixel(a) toInt,0)
//					p.addPoint(0,-inchToPixel(b) toInt)
//					new Area(p)
				val points:List[Vertex] = List((0,0), (a,0), (0,b))
				shapeToArea(new Polygon(points))
			}
			case  Polygon(a: List[Vertex]) => {
				val p = new java.awt.Polygon() ;
				a.map(v => p.addPoint(inchToPixel(v._1) toInt, -inchToPixel(v._2) toInt))
				new Area(p)
			}
			case _ =>  new Area(new Rectangle2D.Double(0,0,0,0))
		}
}


abstract class BasePanel(title: String) extends JPanel with Runnable {
	  val xWin = 800
	  val yWin = 600
	  val transform = new AffineTransform
	  transform.setToTranslation(xWin/2, yWin/2)
	  
	  def doPaint(g: Graphics2D)
	  
	  override def paint(graphics: Graphics) {
		     val g = graphics.asInstanceOf[Graphics2D]
		     g.setPaint(new Color(0, 0, 0))
		     g.fillRect(0, 0, xWin, yWin)
		     g.setTransform(transform)
		     doPaint(g)
      }
		  	  
	  def run() {
		  // NOTE: Picture doesn't have any reactive features, ActionListner and Timer listener are not defined.
		    setPreferredSize(new Dimension(xWin, yWin))
		    setFocusable(true)
		    val frame = new JFrame(title)
		    frame.setLayout(new BorderLayout())
		    frame.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE)
		    frame.setResizable(false)
		    frame.add(this, BorderLayout.CENTER)
		    frame.pack()
		    frame.setVisible(true)
	   }
}

def drawPic( g: Graphics2D, picture: _Picture)  : Graphics2D ={
	  picture match  {
		  case Region(color, region) => drawRegionInWindow(g, color, region)
		  case Over(pic1, pic2) => drawPic(g, pic2); drawPic(g, pic1) //pic1 over pic2
		  case EmptyPic() => return g
	  }
}

def drawRegionInWindow(  g: Graphics2D, color: _Color, region: _Region) : Graphics2D = {
  withColor( g, color,regionToArea(region) )

}

def withColor( g: Graphics2D, color: _Color, area: Area) : Graphics2D = {
	   g.setPaint(color javaColor)
	   g.fill(area)
	   g
}

class PicturePanel(title: String, picture: _Picture) extends BasePanel(title) {
	override def doPaint(g: Graphics2D) {
	 drawPic( g, picture)  // NOTE: picture is set in constructor		  
	}
	
}


def main(args: Array[String]) {
	 draw( "Region Test", pic3Book)
	 draw( "Five Circles", fc)
}
	  
//
//> r1 = Shape (Rectangle 3 2)
//> r2 = Shape (Ellipse 1 1.5)
//> r3 = Shape (RtTriangle 3 2)
//> r4 = Shape (Polygon [(-2.5,2.5), (-3.0,0), (-1.7,-1.0),
//>                      (-1.1,0.2), (-1.5,2.0)] )
//
//> reg1 = r3 `xUnion` (r1 `Intersect` Complement r2 `Union` r4)
//> pic1 = Region Blue reg1
def r1 =  Shape( Rectangle(3,2))
def r2 =  Shape( Ellipse(1, 1.5))
def r3 =  Shape( RtTriangle(3,2))
def r4 =  Shape( Polygon(List((-2.5,2.5), (-3.0,0), (-1.7,-1.0),(-1.1,0.2), (-1.5,2.0))))
def reg1 = r3 xUnion ((r1 Intersect (Complement(r2))) Union r4)

def pic1 =  Region( Blue,  reg1)
//
//> reg2 = let circle = Shape (Ellipse 0.5 0.5)
//>            square = Shape (Rectangle 1 1)
//>        in (Scale (2,2) circle)
//>           `Union` (Translate (1,0) square)
//>           `Union` (Translate (-1,0) square)
//> pic2 = Region Yellow (Translate (0,-1) reg2)
def reg2 = {
	val circle =  Shape( Ellipse( 0.5, 0.5))
	val square =  Shape( Rectangle( 1, 1))
	( Scale( (2,2), circle) Union ( Translate((1,0), square))) Union ( Translate((-1,0), square))
}
def pic2 =  Region( Yellow,  Translate((0, -1), reg2))
//> pic3 = pic2 `Over` pic1
def pic3 = pic2 Over pic1

//> pic3book = pic3 `Over` Region White (Shape (Rectangle 6 5))

def pic3Book = pic3 Over ( Region( White,  Shape(Rectangle(6,5))))

//> r = Shape (Rectangle 2 2)
//> pp1 = Region Red (Translate (1,1) (Scale (0.5,0.5) r))
//> pp2 = Region Blue (Scale (0.5,0.5) (Translate (1,1) r))
//


//> oneCircle   = Shape (Ellipse 1 1)
//> manyCircles = [ Translate (x,0) oneCircle | x <- [0,2..] ]
//> fiveCircles = foldr Union Empty (take 5 manyCircles)
//> fc = Region Red (Scale (0.25,0.25) fiveCircles)
//

def fc = Region( Red,  Scale((0.25, 0.25), fiveCircles))    //five circle was defined in Region.scala

//> r5 = let c1 = Shape (Ellipse 0.5 0.5)
//>          c2 = Translate (1,0) c1
//>          cs = Translate (1,1) (c1 `Union` c2)
//>      in Scale (0.5,0.5) cs
//>
//> r6 = let c = Shape (Ellipse 0.5 0.5)
//>          s = Shape (Rectangle 1 1)
//>      in (Scale (2,2) c) `Union`
//>         ((Translate (2,0) s) `Union` 
//>          (Translate (-2,0) s))
//>
//
//> pictToList :: Picture -> [(Color,Region)]
//>
//> pictToList  EmptyPic      = []
//> pictToList (Region c r)   = [(c,r)]
//> pictToList (p1 `Over` p2) = pictToList p1 ++ pictToList p2
//
//> adjust :: [(Color,Region)] -> Coordinate -> 
//>           (Maybe (Color,Region), [(Color,Region)])
//
//< adjust    []    p
//<    = (Nothing, [])
//< adjust ((c,r):regs) p
//<    = if r `containsR` p then (Just (c,r), regs)
//<      else let (hit, rs) = adjust regs p
//<           in  (hit, (c,r) : rs)
//
//> adjust regs p
//>   = case (break (\(_,r) -> r `containsR` p) regs) of
//>       (top,hit:rest) -> (Just hit, top++rest)
//>       (_,[])         -> (Nothing, regs)
//
//| break (>3) [1..6] ===> ([1,2,3],[4,5,6])
//
//> loop :: Window -> [(Color,Region)] -> IO ()
//>
//> loop w regs = 
//>     do clearWindow w
//>        sequence_ [ drawRegionInWindow w c r | (c,r) <- reverse regs ]
//>        (x,y) <- getLBP w
//>        case (adjust regs (pixelToInch (x - xWin2), 
//>                           pixelToInch (yWin2 - y) )) of
//>          (Nothing,  _      ) -> closeWindow w
//>          (Just hit, newRegs) -> loop w (hit : newRegs)
//
//> draw2 :: String -> Picture -> IO ()
//> draw2 s p
//>   = runGraphics $
//>     do w <- openWindow s (xWin,yWin)
//>        loop w (pictToList p)
//
//> p1,p2,p3,p4 :: Picture
//> p1 = Region Red r1
//> p2 = Region Blue r2
//> p3 = Region Green r3
//> p4 = Region Yellow r4
//
//> pic :: Picture 
//> pic = foldl Over EmptyPic [p1,p2,p3,p4]
//> main = draw2 "Picture Click Test" pic
//
//> p5 = Region White (Shape (Rectangle 6 5))
//> mainbook = draw2 "Picture Click Test" (pic `Over` p5)
//
//< loop w regs
//<    = do clearWindow w
//<         sequence_ 
//<           (map (uncurry (drawRegionInWindow w)) 
//<                (reverse regs))
//<         (x,y) <- getLBP w
//<         let aux (_,r) = r `containsR` ( pixelToInch (x-xWin2), 
//<                                         pixelToInch (yWin2-y) )
//<         case (break aux regs) of
//<           (_,[])        -> closeWindow w
//<           (top,hit:bot) -> loop w (hit : (top++bot))



}
